package afcp.alumni.action.page;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;

import net.sourceforge.stripes.action.Before;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.Message;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.StreamingResolution;
import net.sourceforge.stripes.action.UrlBinding;
import net.sourceforge.stripes.controller.LifecycleStage;
import net.sourceforge.stripes.integration.spring.SpringBean;
import net.sourceforge.stripes.security.Secure;
import net.sourceforge.stripes.validation.LocalizableError;
import net.sourceforge.stripes.validation.Validate;
import net.sourceforge.stripes.validation.ValidateNestedProperties;
import net.sourceforge.stripes.validation.ValidationErrors;
import net.sourceforge.stripes.validation.ValidationMethod;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import afcp.alumni.action.base.PageAction;
import afcp.alumni.action.event.ManageMemberAction;
import afcp.alumni.action.event.UploadMemberFileAction;
import afcp.alumni.dao.base.Dao.KindInfo;
import afcp.alumni.file.ExportService;
import afcp.alumni.model.SearchResult;
import afcp.alumni.model.db.FileColumn;
import afcp.alumni.model.db.Member;
import afcp.alumni.model.db.MemberUtils;
import afcp.alumni.service.UploadService;
import afcp.alumni.service.impl.MemberServiceImpl;
import afcp.alumni.util.Charsets;
import afcp.alumni.util.Conf;

import com.google.appengine.api.utils.SystemProperty;

@UrlBinding(value = AdminAction.URL)
@Secure(roles = Member.ROLE_ADMIN)
public class AdminAction extends PageAction {

	private static final Log logger = LogFactory.getLog(AdminAction.class);

	@Override
	public String getUrl() {
		return URL;
	}

	@Override
	@DefaultHandler
	public Resolution view() {
		return toJsp();
	}

	@Override
	public String getTitle() {
		return msg("admin.title");
	}

	@Override
	protected String defaultJsp() {
		return JSP;
	}

//	@Override
//	@Before
//	protected void setPageVisitedBoolean() {
//		setAdmin();
//	}

	public static final String URL = "/admin";

	// App-relative path to the default JSP file
	private static final String JSP = "/WEB-INF/jsp/admin.jsp";

	@SpringBean
	private MemberServiceImpl memberService;
	@SpringBean
	private UploadService uploadService;
//	@SpringBean
//	private RoleDao roleDao;
	@SpringBean
	private ExportService excelExportService;
	
	
	
	
	
	
	// Add member form

	// These properties are related to the fields with the same name ("name"
	// attribute in the input tags) in the JSP.
	@ValidateNestedProperties({
//			@Validate(field = "login.id", required = true, on = "createMember"),
//			@Validate(field = "firstName", required = true, on = "createMember"),
//			@Validate(field = "lastName", required = true, on = "createMember"),
			//@Validate(field = "password", required = true, on = "createMember", minlength = 2/*, converter = PasswordEncoder.class*/),
//			@Validate(field = "roles", required = true, on = "createMember"),
//			@Validate(field = "birthDate", required = true, on = "createMember"),
			
			@Validate(field = "email", required = true, on = "createMember"),
	})
	private Member newMember;
	// @Validate(field = "role", required = true, on = "createMember")

	/**
	 * Getter to display in the JSP which member has just been added.
	 * 
	 * @return the {@link Member} object of the added Member
	 */
	public Member getNewMember() {
		return newMember;
	}

	/**
	 * Setter to fill the field {@link #newMember} with the form values in the
	 * JSP.
	 * 
	 * @param The
	 *            member set and filled with the form
	 */
	public void setNewMember(Member newMember) {
		this.newMember = newMember;
	}

	private String newMemberOldId;
	public String getNewMemberOldId() {
		return newMemberOldId;
	}
	public void setNewMemberOldId(String newMemberOldId) {
		this.newMemberOldId = newMemberOldId;
	}

//	@Validate(required = true, on = "createMember")
//	private String role;
//
//	public String getRole() {
//		return role;
//	}
//
//	public void setRole(String role) {
//		this.role = role;
//	}
	
//	private Role roleLoaded;

	private boolean added;

	@ValidationMethod(on = "createMember")
	public void completeAndValidateUser(ValidationErrors errors) {
		added = newMemberOldId == null;
		
		// TODO use newMemberOldId to be able to update the email.
		// and change the following code that ignores the given email
//		if (!added) {
//			newMember.setEmail(newMemberOldId);
//		}
		// Commented because a hidden field with name "newMember.email" is
		// already used to pass the form validation.
		
//		newMember.getLogin().setAncestorKey(roleDao.getEntityAncestor().getKey());
		Member foundMember = memberService.getMember(newMember.getId());
		if (added) {
			if (foundMember != null) { // Member already in database
				errors.addGlobalError(new LocalizableError("database.alreadyexists"));
				newMember = null; // Pour ne pas afficher sur la JSP qu'il a été
									// créé
//			} else if (!memberService.roleExists(newMember)) {
//				// Nécessaire pour initialiser l'objet Role
//				errors.addGlobalError(new LocalizableError("database.role.dontexist"));
			} else {
				// The password mustn't be set, even by the administrator
				newMember.setPassword(null);
			}
		} else {
			if (foundMember == null) { // Member not in database
				errors.addGlobalError(new LocalizableError("database.dontexist"));
				newMember = null;
//			} else if (!memberService.roleExists(newMember)) {
//				// Nécessaire pour initialiser l'objet Role
//				errors.addGlobalError(new LocalizableError("database.role.dontexist"));
			} else {
				// The password mustn't be changed, even by the administrator
				newMember.setEncryptedPassword(foundMember.getPassword());
			}
		}
	}

	public static final String NEWMEMBER_MSG_KEY = "newmember";
	
	private static final String NEWMEMBER_BUNDLE = "message.newmember";
	private static final String MODIFMEMBER_BUNDLE = "message.modifiedmember";
	private static final String NEWMEMBERFAIL_BUNDLE = "message.newmember.failed";
	private static final String MODIFMEMBERFAIL_BUNDLE = "message.modifiedmember.failed";
	private static final String OK_BUNDLE = "message.ok";
	private static final String ERROR_BUNDLE = "message.error";
	
	/**
	 * Method called by the new member form. Add a member to the datastore by
	 * calling the service method
	 * {@link MemberServiceImpl#addOrUpdateMember(Member)}.
	 * 
	 * @return
	 */
	public Resolution createMember() {
//		newMember.setRole(roleLoaded);
		// TODO replace with this.getRoles()

		// The member must have already been updated by the validation method,
		// Particularly for the password that mustn't be changed.
		
		// Delegate to the service:
		boolean succeeded = memberService.addOrUpdateMember(newMember);
		Message memberAdded;
		// printing the result
		if (succeeded) {
//			logger.info("Nouveau membre. Prénom : " + newMember.getFirstName()
//			+ ", nom : " + newMember.getLastName());
			if (added) {
				memberAdded = msgObj(NEWMEMBER_BUNDLE, newMember.getFirstName(), newMember.getLastName());
				newMember = null;
			} else {
				memberAdded = msgObj(MODIFMEMBER_BUNDLE, newMember.getFirstName(), newMember.getLastName());
			}
			logger.info(msgToStr(memberAdded));
		} else {
			if (added) {
				memberAdded = msgObj(NEWMEMBERFAIL_BUNDLE, newMember.getFirstName(), newMember.getLastName());
			} else {
				memberAdded = msgObj(MODIFMEMBERFAIL_BUNDLE, newMember.getFirstName(), newMember.getLastName());
			}
			logger.error(msgToStr(memberAdded));
//			String errorMessage = "Le membre " + newMember.getFirstName() + " "
//			+ newMember.getLastName()
//			+ " n'a pas pu être ajouté. Un problème est survenu.";
////			error(errorMessage);
//			logger.error(errorMessage);
		}
		addFlashScope(NEWMEMBER_MSG_KEY, memberAdded);
		return toJsp();
	}

	// End add member form
	
	
	// Modify member

	private String idModified;
	public String getIdModified(){
		return idModified;
	}
	public void setIdModified(String id){
		this.idModified = id;
	}
	
	public Resolution modify(){
		newMember = memberService.getMember(idModified);
		newMemberOldId = newMember.getId();
		return toJsp();
	}
	
	// End modify member
	
	
	// Member list
	public static final String MEMBER_PAGE_PARAM = "pm";
	
	private int pm = 1;
	private int ppm = Integer.parseInt(Conf.get("pagination.perpage"));
	public void setPm(int page) {
		pm = page;
	}
	public void setPpm(int perPage) {
		ppm = perPage;
	}
	private SearchResult<Member> members = null;
	@Before(stages = LifecycleStage.CustomValidation)
	private void loadMembers() {
		if (members == null) {
			members = memberService.getMembersOfPage(pm, ppm);
			members.initPagination(getCurrentUrl(), MEMBER_PAGE_PARAM);
		}
	}

	// delegate method
	@ValidationMethod
	public void checkMemberPageValue(ValidationErrors errors) {
		members.checkPageValue(errors);
	}
	// end delegate methods
	
	public SearchResult<Member> getMembers() {
		return members;
	}
	
	public String getNewMemberKey() {
		return NEWMEMBER_MSG_KEY;
	}
	
	public String getDeleteMemberKey() {
		return ManageMemberAction.DELETEMEMBER_MSG_KEY;
	}
	
//	public String getNewRoleKey() {
//		return ManageMemberAction.NEWROLE_MSG_KEY;
//	}
//	
//	public String getDeleteRoleKey() {
//		return ManageMemberAction.DELETEROLE_MSG_KEY;
//	}

	// End member list
	

	// Reindexation button

	public Resolution reindex() {
		memberService.rebuildIndex();
		return this.toJsp();
	}

	// End of reindexation button
	

	// Role list
	
//	public static final String ROLE_PAGE_PARAM = "pr";
//	
//	private int pr = 1;
//	private int ppr = Integer.parseInt(Conf.get("pagination.perpage"));
//	public void setPr(int page) {
//		pr = page;
//	}
//	public void setPpr(int perPage) {
//		ppr = perPage;
//	}
//	private SearchResult<Role> roles = null;
//	@Before(stages = LifecycleStage.CustomValidation)
//	private void loadRoles() {
//		if (roles == null) {
//			roles = memberService.getRolesOfPage(pr, ppr);
//			roles.initPagination(getCurrentUrl(), ROLE_PAGE_PARAM);
//		}
//	}
//
//	// delegate method
//	@ValidationMethod
//	public void checkRolePageValue(ValidationErrors errors) {
//		roles.checkPageValue(errors);
//	}
//	// end delegate methods
//	
//	public SearchResult<Role> getRoles() {
//		return roles;
//	}
//	public String[] getRoles() {
//		return MemberUtils.ROLES;
//	}

	// End role list
	
	
	
	
	
	
	

	
	// Formulaire de réinitialisation des rôles

//	public Resolution resetRoles() {
//		memberService.resetRoles();
//		return this.toJsp();
//	}

	// End formulaire de réinitialisation des rôles
	
	
	// Formulaire d'upload de fichier CSV contenant une liste de membres
	
//	public String getUploadUrl() {
//		return UploadMemberFileAction.getUploadUrl();
//	}
	public String getUploadUrl() {
		String uploadUrl = uploadService.getUploadUrl(UploadMemberFileAction.URL);
		return uploadUrl;
	}
	
	private List<String> charsets = null;
	public List<String> getCharsets() {
		if (charsets == null) {
			charsets = new LinkedList<>();
			charsets.add(Charsets.CP1252_NAME);
			charsets.add(Charsets.UTF8_NAME);
		}
		return charsets;
	}
	
	private List<String> quotechars = null;
	public List<String> getQuotechars() {
		if (quotechars == null) {
			quotechars = new LinkedList<>();
			quotechars.add("\"");
			quotechars.add("'");
		}
		return quotechars;
	}
	
	private List<String> separators = null;
	public List<String> getSeparators() {
		if (separators == null) {
			separators = new LinkedList<>();
			separators.add(";");
			separators.add(",");
		}
		return separators;
	}
	
	private List<String> escapes = null;
	public List<String> getEscapes() {
		if (escapes == null) {
			escapes = new LinkedList<>();
			escapes.add("\\");
		}
		return escapes;
	}
	
	private List<FileColumn> columns = null;
	public List<FileColumn> getColumns() {
		if (columns == null) {
			columns = memberService.getAllFileColumns();
			Map<String, FileColumn> defaultColumns = null;
			for (FileColumn column : columns) {
				if (column.getPosition() == 0) {
					if (defaultColumns == null) {
						defaultColumns = MemberUtils.defaultColumnsMap();
					}
					int defaultPosition = defaultColumns.get(column.getId()).getPosition();
					column.setPosition(defaultPosition);
				}
			}
		}
		return columns;
	}
	
	// End upload de membres
	
	
	// test mail
	
//	private MailService mailService = SF.mail();
//	public Resolution sendmail() {
//		if (Conf.debug()) {
//			try {
//				Member member = new Member();
//				member.setFirstName("Antoine");
//				member.setLastName("ORY-LAMBALLE");
//				member.setEmail("antoine.orylamballe@yahoo.fr");
//				member.setLogin(new Login("aorylamballe"));
////			mailService.sendPasswordToMember(member, "Nouv3auM0tdePasse");
//				try {
//					new PasswordEmail(member, "Nouv3auM0tdePasse", context.getLocale()).send();
//				} catch (MessagingException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				}
//			} catch (Exception e) {
//				// TODO Auto-generated catch block
//				e.printStackTrace();
//			}
//		}
//		return toJsp();
//	}
	
	// fin test mail
	
	public String getFlashscopeKey() {
		return PROBLEM_FLASHSCOPE_KEY;
	}
	
	private static final String XLSX_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
	private static final DateFormat fileDateFormat = new SimpleDateFormat("yyyy-MM-dd");
	
	public Resolution export() {
		String today = fileDateFormat.format(new Date());
		return new StreamingResolution(XLSX_CONTENT_TYPE) {

			@Override
			protected void stream(HttpServletResponse response)
					throws Exception {
				excelExportService.download(response.getOutputStream());
			}
			
		}.setFilename(today + " AFCP Alumni Members");
	}
	
	public Resolution clear() {
		Message memberAdded;
		
		try {
			memberService.clearSessionsAndCache();
			memberAdded = msgObj(OK_BUNDLE);
			
		} catch (Exception e) {
			memberAdded = msgObj(ERROR_BUNDLE);
			logger.error(msgToStr(memberAdded));
			logger.error("Error while clearing sessions and cache.", e);
		}
		
		addFlashScope(NEWMEMBER_MSG_KEY, memberAdded);
		
		return this.toJsp();
	}
	
	private List<KindInfo> kinds = null;
	public List<KindInfo> getKinds() {
		if (kinds == null) {
			kinds = memberService.findKinds();
		}
		return kinds;
	}
	
	private List<KindInfo> kindsWithStats = null;
	public List<KindInfo> getKindsWithStats() {
		if (kindsWithStats == null) {
			kindsWithStats = memberService.findKindsWithStats();
		}
		return kindsWithStats;
	}
	
	public boolean isProduction() {
		return SystemProperty.environment.value() ==
			    SystemProperty.Environment.Value.Production;
	}
	public boolean isDevelopment() {
		return SystemProperty.environment.value() ==
			    SystemProperty.Environment.Value.Development;
	}
	
	private String kindName;

	public void setKindName(String kindName) {
		this.kindName = kindName;
	}

	public Resolution remove() {
		try {
			memberService.removeByKindName(kindName, getContext());
		} catch (Exception e) {
			logger.error("Error while removing entity " + kindName, e);
		}
		if (getContext().getUser() == null) {
			return this.redirect();
		} else {
			return this.toJsp();
		}
	}
	
	public Resolution clearall() {
		memberService.clearAllDatastore(getContext());
		return this.redirect();
	}

}
